package com.zgg.core.verticle

import com.zgg.MainStart
import com.zgg.core.annotation.HttpHandler
import com.zgg.core.annotation.HttpMethod
import com.zgg.core.config.RunStartScanHandler
import com.zgg.core.exception.ClientException
import com.zgg.core.utils.Reflection
import io.vertx.core.AbstractVerticle
import io.vertx.core.Handler
import io.vertx.core.http.HttpServer
import io.vertx.ext.web.Route
import io.vertx.ext.web.Router
import io.vertx.ext.web.RoutingContext
import io.vertx.ext.web.handler.BodyHandler
import io.vertx.ext.web.handler.LoggerHandler
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlin.reflect.KClass
import kotlin.reflect.KParameter
import kotlin.reflect.full.callSuspendBy
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.functions

class HttpWebVerticle : AbstractVerticle() {
    lateinit var httpServer: HttpServer
    lateinit var rootRouter: Router

    override fun start() {
        httpServer = vertx.createHttpServer()
        rootRouter = Router.router(vertx)
        rootRouter.route().failureHandler { rc ->
            var statusCode = 500
            if (rc.failure() is ClientException) {
                statusCode = 400
            }
            rc.response().setStatusCode(statusCode).end(rc.failure().message)
        }
        rootRouter.route().handler(BodyHandler.create().setDeleteUploadedFilesOnEnd(true))
        rootRouter.route().handler(LoggerHandler.create())
        scanHttpHandler()
        httpServer.requestHandler(rootRouter::accept).listen(521)
    }

    @Throws(RuntimeException::class)
    fun scanHttpHandler() {
        try {
            val pkg = MainStart.appClass!!.getPackage().name
            val handlerClass: Set<KClass<*>> = Reflection.findHandlerKClass(pkg)
            for (a in handlerClass) {
                val classRouter: Router = Router.router(MainStart.vertx)
                for (method in a.functions) {
                    val annotation: HttpHandler? = method.findAnnotation()
                    if (annotation != null) {
                        //todo 解析方法所需参数。
                        val parameters: List<KParameter> = method.parameters
                        //实例化对象
                        val o: Any = a.createInstance()
                        val handler: Handler<RoutingContext> = Handler<RoutingContext> { rc ->
                            GlobalScope.launch {
                                try {
                                    //before
                                    val args = RunStartScanHandler.parseArgs(parameters, rc, o)
                                    //handler
                                    val invoke = if (method.isSuspend) {
                                        method.callSuspendBy(args)
                                    } else {
                                        method.callBy(args)
                                    }
                                    //after
                                    RunStartScanHandler.parseReturnValue(rc, invoke, annotation)
                                } catch (e: Exception) {
                                    throw RuntimeException(e)
                                }
                            }
                        }
                        val path: String = annotation.path
                        val httpMethods: Array<HttpMethod> = annotation.method
                        //方法路由
                        var route: Route = if (httpMethods.size != 1) {
                            classRouter.route(path)
                        } else {
                            classRouter.route(
                                    io.vertx.core.http.HttpMethod.valueOf(java.lang.String.valueOf(httpMethods[0])),
                                    path
                            )
                        }
                        if (annotation.isBlocking) {
                            route.blockingHandler(handler)
                        } else {
                            route.handler(handler)
                        }
                    }
                }
                //类上的注解，只有path有效
                val classHttpHandler: HttpHandler? = a.findAnnotation()
                val path = classHttpHandler?.path ?: "/"
                rootRouter.mountSubRouter(path, classRouter)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            MainStart.logger.error("scan handler failed.", e.cause)
            vertx.close()
        }
    }
}